package mapx

//
func CopyNoCover(from, to map[string]interface{}) {
	copyMap(from, to, false)
	return
}

//
func Copy(from, to map[string]interface{}) {
	copyMap(from, to, true)
	return
}

// isCover 如果目标已经存在值，是否覆盖
func copyMap(from, to map[string]interface{}, isCover bool) (r interface{}) {
	for key, val := range from {
		toVal, has := to[key]
		if has && isCover == false {
			_, ok := toVal.(map[string]interface{})
			if !ok {
				continue
			}
			if toVal == nil {
				continue
			}
		}

		to[key] = copyAny(val, toVal, isCover)
	}
	return to
}

func copyArray(from, to []interface{}, isCover bool) (r []interface{}) {
	for i, val := range from {
		to[i] = copyAny(val, to[i], isCover)
	}
	return to
}

func copyAny(fromVal, toVal interface{}, isCover bool) (r interface{}) {
	switch fv := fromVal.(type) {
	case map[string]interface{}:
		var m map[string]interface{}
		if fv == nil {
			return m
		}
		if toVal == nil {
			toVal = m
		}
		switch tv := toVal.(type) {
		case map[string]interface{}:
			if tv == nil {
				tv = map[string]interface{}{}
			}
			r = copyMap(fv, tv, isCover)
		case IFromMap:
			tv.FromMap(fv)
		default:
			panic("to val is not map[string]interface{}")
		}
	case []interface{}:
		toVal = make([]interface{}, len(fv), cap(fv))
		switch tv := toVal.(type) {
		case []interface{}:
			r = copyArray(fv, tv, isCover)
		default:
			panic("to val is not []interface{}")
		}
	default:
		//fmt.Printf("clone any: %T, %v\n", fv, fv)
		r = fv
	}
	return
}
